import { BufferAttribute3D, BufferGeometry3D, Color, Float32BufferAttribute3D, FramebufferTexture3D, Line3D, LineBasicMaterial3D, Node3D, OrthographicCamera3D, PerspectiveCamera3D, Scene3D, Sprite3D, SpriteMaterial3D, Vector2, WebGLRenderer3D } from 'feng3d-three';
import '../../extension/controls/OrbitControls3D';
import { GeometryUtils3D } from '../../extension/utils/GeometryUtils3D';

let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;
let line: Line3D;
let sprite: Sprite3D;
let texture: FramebufferTexture3D;

let cameraOrtho: OrthographicCamera3D;
let sceneOrtho: Scene3D;

let offset = 0;

const dpr = window.devicePixelRatio;

const textureSize = 128 * dpr;
const vector = new Vector2();
const color = new Color();

init();
animate();

function init()
{
    //

    const width = window.innerWidth;
    const height = window.innerHeight;

    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 70, aspect: width / height, near: 1, far: 1000 });
    camera.entity.position.z = 20;

    cameraOrtho = new Node3D().addComponent('OrthographicCamera3D', { left: -width / 2, right: width / 2, top: height / 2, bottom: -height / 2, near: 1, far: 10 });
    cameraOrtho.entity.position.z = 10;

    scene = new Node3D().addComponent('Scene3D');
    sceneOrtho = new Node3D().addComponent('Scene3D');

    //

    const points = GeometryUtils3D.gosper(8);

    const geometry = new BufferGeometry3D();
    const positionAttribute = new Float32BufferAttribute3D(points, 3);
    geometry.setAttribute('position', positionAttribute);
    geometry.center();

    const colorAttribute = new BufferAttribute3D(new Float32Array(positionAttribute.array.length), 3);
    colorAttribute.setUsage('DynamicDrawUsage');
    geometry.setAttribute('color', colorAttribute);

    const material = new LineBasicMaterial3D({ vertexColors: true });

    line = new Node3D().addComponent('Line3D', { geometry, material });
    line.entity.scale.setScalar(0.05);
    scene.entity.addChild(line.entity);

    //

    texture = new FramebufferTexture3D(textureSize, textureSize, 'RGBAFormat');
    texture.minFilter = 'NearestFilter';
    texture.magFilter = 'NearestFilter';

    //

    const spriteMaterial = new SpriteMaterial3D({ map: texture });
    sprite = new Node3D().addComponent('Sprite3D', { material: spriteMaterial });
    sprite.entity.scale.set(textureSize, textureSize, 1);
    sceneOrtho.entity.addChild(sprite.entity);

    updateSpritePosition();

    //

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.autoClear = false;
    document.body.appendChild(renderer.domElement);

    //

    const selection = document.getElementById('selection');
    const controls = scene.addComponent('OrbitControls3D', { camera, domElement: selection });
    controls.enablePan = false;

    //

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    const width = window.innerWidth;
    const height = window.innerHeight;

    camera.aspect = width / height;
    camera.updateProjectionMatrix();

    cameraOrtho.left = -width / 2;
    cameraOrtho.right = width / 2;
    cameraOrtho.top = height / 2;
    cameraOrtho.bottom = -height / 2;
    cameraOrtho.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

    updateSpritePosition();
}

function updateSpritePosition()
{
    const halfWidth = window.innerWidth / 2;
    const halfHeight = window.innerHeight / 2;

    const halfImageWidth = textureSize / 2;
    const halfImageHeight = textureSize / 2;

    sprite.entity.position.set(-halfWidth + halfImageWidth, halfHeight - halfImageHeight, 1);
}

function animate()
{
    requestAnimationFrame(animate);

    const colorAttribute = line.geometry.getAttribute('color');
    updateColors(colorAttribute as BufferAttribute3D);

    // scene rendering

    renderer.clear();
    renderer.render(scene, camera);

    // calculate start position for copying data

    vector.x = (window.innerWidth * dpr / 2) - (textureSize / 2);
    vector.y = (window.innerHeight * dpr / 2) - (textureSize / 2);

    renderer.copyFramebufferToTexture(vector, texture);

    renderer.clearDepth();
    renderer.render(sceneOrtho, cameraOrtho);
}

function updateColors(colorAttribute: BufferAttribute3D)
{
    const l = colorAttribute.count;

    for (let i = 0; i < l; i++)
    {
        const h = ((offset + i) % l) / l;

        color.setHSL(h, 1, 0.5);
        colorAttribute.setX(i, color.r);
        colorAttribute.setY(i, color.g);
        colorAttribute.setZ(i, color.b);
    }

    colorAttribute.needsUpdate = true;

    offset -= 25;
}
